// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"
#include "thread.hpp"
#include <memory>


namespace
{
	/// The thread procedure used to run the thread.
	void thread_proc(void * param)
	{
		internal::use_new_apartment a;
		std::auto_ptr<var> thread(static_cast<var*>(param));

		try
		{
			stack_trace_entry e1("thread");
			// No need to proxy the return value since the thread pointer is already a proxy
			(*thread)["return"] = (*thread)["Thread::function"]();
		}
		catch(var & v)
		{
			(*thread)["exception"] = v;
		}
		catch(std::exception&ex)
		{
			(*thread)["exception"] = exception("std::exception", ex.what());
		}
		catch(...)
		{
			(*thread)["exception"] = exception("...");
		}
	}

	/// Waits for the thread to join, returns the result or throws an exception.
	var thread_join(var thread)
	{
		{
			internal::leave_apartment l;
			thread["Thread::object"].as<api::thread>().join();
		}
		if( thread.contains("exception") ) 
		{
			throw var(thread["exception"]);
		}
		return thread["return"];
	}

	// Creates a new thread.
	/** @param fn is the function to execute in the thread */
	var thread_create(var fn)
	{
		var thread_t = dynamic::create<api::thread>();

		var thread = object("thread").extend
			("Thread::function", fn)
			("join", thread_join)
			("Thread::object", thread_t);

		/// \todo Handle a failure properly (there's a potential memory leak).
		thread_t.as<api::thread>().run(thread_proc, new var( thread.proxy() ) );
		
		return thread;
	}

	void event_wait(var event)
	{
		internal::leave_apartment l;
		event["Event::object"].as<api::event>().wait();
	}

	void event_signal(var event)
	{
		event["Event::object"].as<api::event>().signal();
	}

	var event_create()
	{
		return object("event").extend
			("Event::object", dynamic::create<api::event>())
			("wait", event_wait)
			("signal", event_signal);
	}

	void mutex_lock(var mutex)
	{
		mutex["mutex"].as<api::mutex>().lock();
	}

	void mutex_unlock(var mutex)
	{
		mutex["mutex"].as<api::mutex>().unlock();
	}
}


var dynamic::event()
{
	return event_create();
}


var dynamic::thread(const var & fn)
{
	return thread_create(fn);
}


void dynamic::sleep(var const & s)
{
	internal::leave_apartment l;
	api::sleep(s.as_int());
}


var dynamic::mutex()
{
	return object("mutex").extend
		("lock", mutex_lock)
		("unlock", mutex_unlock)
		("mutex", create<api::mutex>());
}

